﻿using System;
using System.Collections.Generic;
using System.IO.Ports;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Runtime.ConstrainedExecution;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace ABELGX
{
    public class Port
    {
        //解决错误 ：UDP错误10054:远程主机强迫关闭了一个现有的连接
        const uint IOC_IN = 0x80000000;
        static int IOC_VENDOR = 0x18000000;
        int SIO_UDP_CONNRESET = (int)(IOC_IN | IOC_VENDOR | 12);

        public PortSet Set{ get; set; }

        public byte[] Buffer { get; set; } = new byte[8192];
        public int BufferStart { get; set; }
        public int BufferEnd { get; set; }
        public int BufferLength => BufferEnd - BufferStart;
        public DateTime SendTime { get; set; }

        IPEndPoint remote;
        UdpClient udp;
        SerialPort serialPort1;
        bool run = true;
        public Port(PortSet set)
        {
            Set = set;
            try
            {
                if (Set.IsCom)
                {
                    serialPort1 = new SerialPort();
                    serialPort1.DataReceived += SerialPort1_DataReceived;
                    serialPort1.PortName = Set.ComName;
                    serialPort1.BaudRate = Set.BaudRate;
                    serialPort1.Open();
                }
                else if (Set.LocalPort > 0 && Set.LocalPort < 65536)
                {
                    IPAddress local;
                    if (IPAddress.TryParse(Set.LocalIP, out local))
                    {
                        IPEndPoint ip = new IPEndPoint(local, Set.LocalPort);
                        udp = new UdpClient(ip);
                    }
                    else
                    {
                        udp = new UdpClient(Set.LocalPort);
                    }
                    udp.Client.IOControl((int)SIO_UDP_CONNRESET, new byte[] { Convert.ToByte(false) }, null);
                    remote = new IPEndPoint(IPAddress.Parse(Set.RemoteIP), Set.RemotePort);
                    Task.Run(() => udpReceive());
                }
            }
            catch
            {

            }
        }
        public void Clear()
        {
            BufferStart = 0;
            BufferEnd = 0;
        }
        public void Close()
        {
            run = false;
            if(serialPort1!= null)
            {
                serialPort1.Close();
                serialPort1 = null;
            }
            if (udp != null)
            {
                //UdpClient udpClient = new UdpClient(35421);
                //udpClient.Connect("127.0.0.1", Set.LocalPort);
                //byte[] bytes = new byte[] { 0x00};
                //udpClient.Send(bytes, bytes.Length);
                //udpClient.Close();
                //udpClient.Dispose();

                udp.Close();
                udp.Dispose();
                udp = null;
            }
        }
        private void SerialPort1_DataReceived(object sender, SerialDataReceivedEventArgs e)
        {
            try
            {
                int read_len = serialPort1.Read(Buffer, BufferEnd, Buffer.Length - BufferEnd);
                if(!Set.Receive)
                {
                    return;
                }
                BufferEnd += read_len;
                if (DataReceive != null)
                {
                    DataReceive(this, null);
                }
            }
            catch
            {

            }
        }

        void udpReceive()
        {
            IPEndPoint ip = null;
            while (run)
            {
                try
                {
                    byte[] recData = udp.Receive(ref ip);
                    if (!Set.Receive)
                    {
                        continue;
                    }
                    Array.Copy(recData, 0, Buffer, BufferEnd, recData.Length);
                    BufferEnd += recData.Length;
                    //Thread.Sleep(1000);
                    if (DataReceive != null)
                    {
                        DataReceive(this, null);
                    }
                }
                catch
                {
                    Thread.Sleep(1000);
                }
            }
        }

        public void Send(byte[] data)
        {
            if (!Set.Send || data == null || data.Length == 0)
            {
                return;
            }
            if (serialPort1 != null)
            {
                if (serialPort1.IsOpen)
                {
                    serialPort1.Write(data, 0, data.Length);
                }
            }
            else if (udp == null)
            {
                udp.Send(data, data.Length, remote);
            }
        }

        public event EventHandler<EventArgs> DataReceive;

    }
}
